#include <math.h>
#include <vector>
#include <string>
#include <GL/gl.h>
#include <GL/glu.h>
#include "SDL/SDL.h"
#include "SDL/SDL_image.h"
#include "SDL/SDL_ttf.h"
#include "texture.h"

using namespace std;

vector<string> texnames;
vector<Texture*> textures;
vector<string> textexts;
vector<Texture*> texttextures;
TTF_Font* font;

void initTexture() {
	font = TTF_OpenFont("Arial.ttf", 16);
	if(!font)
	{
		printf("TTF_OpenFont: %s\n", TTF_GetError());
		exit(1);
	}
}

void finiTexture() {
	TTF_CloseFont(font);
}

scale SizeTextTexture(string text, int* x, int* y) {
	TTF_SizeText(font, text.c_str(), x, y);
}

scale BindImageTexture(string name) {
	for(int i = 0; i < texnames.size(); i++)
	{
		if(name == texnames[i])
		{
			textures[i]->Bind();
			return textures[i]->Scale();
		}
	}

	textures.push_back(TexturefromImage(name));
	texnames.push_back(name);

	textures[textures.size()-1]->Bind();

	return textures[textures.size()-1]->Scale();
}

scale BindTextTexture(string text)
{
	for(int i = 0; i < textexts.size(); i++)
	{
		if(text == textexts[i])
		{
			texttextures[i]->Bind();
			return texttextures[i]->Scale();
		}
	}

	texttextures.push_back(TexturefromText(text));
	textexts.push_back(text);

	texttextures[texttextures.size()-1]->Bind();

	return texttextures[texttextures.size()-1]->Scale();
}

void ClearImageTexture(string name)
{
	for(int i = 0; i < textures.size(); i++)
	{
		if(name == texnames[i])
		{
			delete textures[i];
			textures.erase(textures.begin() + i);
			texnames.erase(texnames.begin() + i);
			return;
		}
	}
}

void ClearTextTexture(string text)
{
	for(int i = 0; i < texttextures.size(); i++)
	{
		if(text == textexts[i])
		{
			delete textures[i];
			textexts.erase(textexts.begin() + i);
			texttextures.erase(texttextures.begin() + i);
			return;
		}
	}
}

void ClearAllTextures()
{
	for(int i = 0; i < textures.size(); i++)
	{
		delete textures[i];
	}

	for(int i = 0; i < texttextures.size(); i++)
	{
		delete texttextures[i];
	}

	textures.clear();
	texnames.clear();
	textexts.clear();
	texttextures.clear();
}

SDL_Surface* make_powerof2(SDL_Surface* surf)
{
  Uint32 rmask, gmask, bmask, amask;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
  rmask = 0xff000000;
  gmask = 0x00ff0000;
  bmask = 0x0000ff00;
  amask = 0x000000ff;
#else
  rmask = 0x000000ff;
  gmask = 0x0000ff00;
  bmask = 0x00ff0000;
  amask = 0xff000000;
#endif
  
  if( pow(2, ceil(log(surf->w)/log(2))) == surf->w && 
      pow(2, ceil(log(surf->h)/log(2))) == surf->h )
    {
      return surf;
    }
  else
    {
      SDL_Surface* ret = SDL_CreateRGBSurface(SDL_SWSURFACE|SDL_SRCALPHA, 
					      (int) pow(2, ceil(log(surf->w)/log(2))),
					      (int) pow(2, ceil(log(surf->h)/log(2))), 32,
					      rmask, gmask, bmask, amask);
      
      SDL_SetAlpha(surf, 0, 0);
      SDL_BlitSurface(surf, NULL, ret, NULL);
      SDL_FreeSurface(surf);
      return ret;
    }
}

Texture* TexturefromImage(string name)
{
	SDL_Surface* surf = IMG_Load(name.c_str());
	if(!surf)
	{
		printf("Could not load image %s\n", name.c_str());
	}
	return new Texture(surf);
}

Texture* TexturefromText(string s)
{


	SDL_Surface* surf;
	SDL_Color color = {255,255,255,255};
	surf = TTF_RenderText_Blended(font, s.c_str(), color);
	if(!surf)
	{
		printf("TTF_RenderText_Blended: %s\n", TTF_GetError());
		TTF_CloseFont(font);
		return NULL;
	}
	return new Texture(surf);
}

Texture::Texture(SDL_Surface* image)
{
	int x = image->w;
	int y = image->h;
	image = make_powerof2(image);
	xscale = (float)x/image->w;
	yscale = (float)y/image->h;

	glGenTextures(1, (GLuint*)&tex_num);
	glBindTexture(GL_TEXTURE_2D, tex_num);
	gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, image->w, image->h, 
					  GL_RGBA, GL_UNSIGNED_BYTE, image->pixels);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
	SDL_FreeSurface(image);
}

Texture::~Texture()
{
	glDeleteTextures(1, (GLuint*)&tex_num);
}

void Texture::Bind()
{
  glBindTexture(GL_TEXTURE_2D, tex_num);
  glEnable(GL_TEXTURE_2D);
}

void Texture::Render(float x1, float y1, float x2, float y2)
{
  this->Bind();
  glBegin(GL_QUADS);
  glTexCoord2f(0.0, yscale);
  glVertex2f(x1, y1);
  glTexCoord2f(0.0, 0.0);
  glVertex2f(x1, y2);
  glTexCoord2f(xscale, 0.0);
  glVertex2f(x2, y2);
  glTexCoord2f(xscale, yscale);
  glVertex2f(x2, y1);
  glEnd();
  glDisable(GL_TEXTURE_2D);
}

scale Texture::Scale()
{
	scale ret;
	ret.xscale = xscale;
	ret.yscale = yscale;
	return ret;
}

